package org.sl.flvplayer.controls {
	import caurina.transitions.Tweener;

	import org.sl.flvplayer.elements.ControllerElement;
	import org.sl.flvplayer.elements.PlayControllerElement;
	import org.sl.flvplayer.elements.SeekControllerElement;
	import org.sl.flvplayer.elements.VolumeControllerElement;
	import org.sl.flvplayer.elements.library.FullscreenOffSkin;
	import org.sl.flvplayer.elements.library.FullscreenSkin;
	import org.sl.flvplayer.elements.library.PauseSkin;
	import org.sl.flvplayer.elements.library.PlaySkin;
	import org.sl.flvplayer.elements.library.ScreenPauseSkin;
	import org.sl.flvplayer.elements.library.ScreenPlaySkin;
	import org.sl.flvplayer.elements.library.SeekbarSkin;
	import org.sl.flvplayer.elements.library.SoundMuteSkin;
	import org.sl.flvplayer.elements.library.SoundSkin;
	import org.sl.flvplayer.elements.library.VolumeSkin;
	import org.sl.flvplayer.flyweights.FWPlayerCommand;
	import org.sl.flvplayer.view.PlayerScreen;

	import flash.display.Sprite;
	import flash.display.StageDisplayState;
	import flash.events.Event;
	import flash.events.MouseEvent;

	/**
	 * Is the base class for all UI elements and handles positioning and view of them.</br> 
	 * Each button element can have different skins attached to it and thus can be freely skinned.</br>
	 * Furthermore there is the posibility to bind a command to each button and so enable controlling of the playerapplication.
	 * 
	 * @package org.sl.flvplayer
	 * @author Dan
	 * @version
	 * @see org.sl.flvplayer.controls.PlayerLogic
	 * @see org.sl.flvplayer.elements.ControllerElement
	 * @see org.sl.flvplayer.elements.PlayControllerElement
	 * @see org.sl.flvplayer.elements.SeekControllerElement
	 * @see org.sl.flvplayer.elements.VolumeControllerElement
	 * @see org.sl.flvplayer.view.PlayerScreenInteraction
	 */ 
	public class Controller extends Sprite {

		private static var instance : Controller;
		private static var staticElementsWidth : Number;

		private var elementList : Array = new Array();
		private var btnPlay : ControllerElement = new ControllerElement();
		private var btnFull : ControllerElement = new ControllerElement();			
		private var btnMute : ControllerElement = new ControllerElement();
		private var btnVolume : ControllerElement = new VolumeControllerElement(true);
		
		/**
		 * playerScreen btnScreenPlay button, is public because of the access of the PlayerScreenInteraction class
		 */
		public var btnScreenPlay : ControllerElement = new  PlayControllerElement(true);
		
		/**
		 * btnSeek is public because its width 
		 */
		public var btnSeek : ControllerElement = new SeekControllerElement(true);

		/**
		 * Ensures that there is only one instance of the class by using the singleton pattern.
		 * 
		 * @return instance of the <code>Controller</code> class
		 */
		public static function getInstance() : Controller {
			if(!instance) instance = new Controller();			
			return instance;
		}
		
		/**
		 * Controller constructor adds the btnScreenPlay element by default.
		 */
		function Controller() {
			super();
			addScreenyPlaybtn();
		}

		/**
		 * Initializes all functions necessary to render the GUI.
		 */
		public function render() : void {										
			addPlaybtn();
			addFullSreenbtn();		
			addVolumebtn();
			addMutebtn();
			addSeekbtn();		
			removeDuplicate(elementList);
			sumElementsWitdh();	
			updatePos();
			initSeek();	
			initStageListeners();
		}

		/**
		 * Playbutton.
		 */
		public function addPlaybtn() : void {
			btnPlay.command = FWPlayerCommand.PAUSE;
			btnPlay.addSkin(new PlaySkin(), "play");
			btnPlay.addSkin(new PauseSkin(), "pause");
			btnPlay.showSkin("play");
			elementList.push({type:"play", element:btnPlay});
			elementList.push({type:"pause", element:btnPlay});
			btnPlay.addEventListener(MouseEvent.CLICK, onInteraction);
			addChild(btnPlay);
		}

		/**
		 * Playbutton on the playerscreen.
		 */
		public function addScreenyPlaybtn() : void {
			btnScreenPlay.command = FWPlayerCommand.PAUSE;
			btnScreenPlay.addSkin(new ScreenPlaySkin(), "screen_play");
			btnScreenPlay.addSkin(new ScreenPauseSkin(), "screen_pause");
			btnScreenPlay.showSkin("screen_play");
			elementList.push({type:"screen_play", element:btnScreenPlay});
			elementList.push({type:"screen_pause", element:btnScreenPlay});
			btnScreenPlay.addEventListener(MouseEvent.CLICK, onInteraction);
			addChild(btnScreenPlay);
		}

		/**
		 * Button for the fullscreenmode.
		 */
		public function addFullSreenbtn() : void {
			btnFull.command = FWPlayerCommand.FULLSCREEN;
			btnFull.addSkin(new FullscreenSkin(), "fullscreen");
			btnFull.addSkin(new FullscreenOffSkin(), "fullscreenOff");
			btnFull.showSkin("fullscreen");
			elementList.push({type:"fullscreen", element:btnFull});
			elementList.push({type:"fullscreenOff", element:btnFull});
			btnFull.addEventListener(MouseEvent.CLICK, onInteraction);
			addChild(btnFull);
		}

		/**
		 * Volumebutton to alter the volume.
		 */
		public function addVolumebtn() : void {
			btnVolume.addSkin(new VolumeSkin(), "volume");
			btnVolume.showSkin("volume");
			elementList.push({type:"volume", element:btnVolume});
			btnVolume.addEventListener(MouseEvent.MOUSE_OUT, hideVolumeBar);
			btnVolume.addEventListener(MouseEvent.MOUSE_OVER, showVolumeBar);
			addChild(btnVolume);
		}

		/**
		 * Mutebutton.
		 */
		public function addMutebtn() : void {
			btnMute.command = FWPlayerCommand.MUTE;
			btnMute.addSkin(new SoundSkin(), "sound");
			btnMute.addSkin(new SoundMuteSkin(), "mute");
			btnMute.showSkin("sound");
			elementList.push({type:"sound", element:btnMute});
			elementList.push({type:"mute", element:btnMute});
			btnMute.addEventListener(MouseEvent.CLICK, onInteraction);
			btnMute.addEventListener(MouseEvent.MOUSE_OVER, showVolumeBar);
			btnMute.addEventListener(MouseEvent.MOUSE_OUT, hideVolumeBar);
			addChild(btnMute);	
		}

		/**
		 * Seekbar with time display.
		 */
		public function addSeekbtn() : void {
			btnSeek.addSkin(new SeekbarSkin(), "seek");
			btnSeek.showSkin("seek");
			elementList.push({type:"seek", element:btnSeek});
			addChild(btnSeek);
		}
		
		/**
		 * Adds stage listeners for mouse interaction.
		 */
		private function initStageListeners() : void {
			stage.addEventListener(Event.MOUSE_LEAVE, stageLeaveHandler);
			stage.addEventListener(MouseEvent.MOUSE_OVER, stageEnterHandler);
		}

		/**
		 * Handles which skin is shown depending on p_type and p_state of the relevant element.</br>
		 * In addition multiple skins can be attached to an buttonelement.
		 * 
		 * @param p_state indicates whether the skin is displayed or not
		 * @param p_type type of skin	 
		 */
		public function changeStatus(p_type : String, p_state : Boolean) : void {
			var i : Object;
			switch (p_type) {
				case "play":
					for each(i in elementList) {
						if (i.type == p_type && p_state) ControllerElement(i.element).showSkin(p_type);
					}
					break;
				case "pause":
					for each(i in elementList) {
						if (i.type == p_type && p_state) ControllerElement(i.element).showSkin(p_type);
					}
					break;
				case "fullscreen":	
					for each(i in elementList) {
						if (i.type == p_type && p_state) ControllerElement(i.element).showSkin(p_type);
					}
					break;
				case "fullscreenOff":	
					for each(i in elementList) {
						if (i.type == p_type && p_state) ControllerElement(i.element).showSkin(p_type);
					}
					break;
				case "sound":	
					for each(i in elementList) {
						if (i.type == p_type && p_state) ControllerElement(i.element).showSkin(p_type);
					}
					break;
				case "mute":	
					for each(i in elementList) {
						if (i.type == p_type && p_state) ControllerElement(i.element).showSkin(p_type);
					}
					break;
				case "screen_play":	
					for each(i in elementList) {
						if (i.type == p_type && p_state) ControllerElement(i.element).showSkin(p_type);
					}
					break;
				case "screen_pause":	
					for each(i in elementList) {
						if (i.type == p_type && p_state) ControllerElement(i.element).showSkin(p_type);
					}
					break;
			}
		}

		/**
		 * Updates the position of the UI elements.</br>
		 * This method is also used for positioning when the playerapplication is resized.
		 */
		public function updatePos() : void {						
			// to prevent false positioning of the volumebar all tweens are removed during positioning 
			Tweener.removeTweens(btnVolume);
		
			btnPlay.y = stage.stageHeight - btnPlay.height;			
			
			btnFull.x = stage.stageWidth - btnFull.width;	
			btnFull.y = stage.stageHeight - btnFull.height;	
			
			btnMute.x = btnFull.x - btnMute.width ;
			btnMute.y = stage.stageHeight - btnMute.height;	
		
			btnVolume.x = btnMute.x;
			btnVolume.y = btnMute.y;
			
			btnSeek.x = btnPlay.width;	
			btnSeek.y = stage.stageHeight - btnSeek.height;	
			btnSeek.width = stage.stageWidth - staticElementsWidth;
			
			btnScreenPlay.x = (stage.stageWidth / 2) - ( btnScreenPlay.width / 2);
			btnScreenPlay.y = (stage.stageHeight / 2) - ( btnScreenPlay.height / 2 );
		}

		/**
		 * Removes all the UI elements except the btnScreenPlay element.
		 */
		public function removeControls() : void {
			removeChild(btnPlay);
			removeChild(btnFull);
			removeChild(btnVolume);
			removeChild(btnMute);
			removeChild(btnSeek);
		}

		/**
		 * Sums up the total witdh of all UI elements which aren`t scaleable.</br>
		 * The only element which should be scaled is the seekbar and therefor the sum of all nonscaled elements must be calculated to determine the width of the btnSeek element. 
		 */
		private function sumElementsWitdh() : void {		
			var sum : Number = 0;
			var element : ControllerElement;
			for each(var obj : Object in removeDuplicate(elementList) ) {
				element = obj.element as ControllerElement;
				// checks whether the current element is scaleable 
				if (!element.allowScale) sum += element.width; 
			}			
			staticElementsWidth = sum ;
		}

		/**
		 * Shows the volumebar using a tweening.
		 * 
		 * @param p_event
		 */
		private function showVolumeBar(p_event : MouseEvent) : void {								
			Tweener.addTween(btnVolume, {alpha:1, time:0.5, delay:0.5, x:btnMute.x, y:btnMute.y - btnVolume.height});	
		}

		/**
		 * Hides the volumebar using a tweening.
		 * 
		 * @param p_event
		 */
		private function hideVolumeBar(p_event : MouseEvent) : void {					
			Tweener.addTween(btnVolume, {alpha:1, time:0.5, delay:0.5, x:btnMute.x, y:btnMute.y + btnVolume.height});					
		}
		
		/**
		 * Mouse leaves stage handler.
		 * 
		 * @param p_event
		 */
		private function stageLeaveHandler(p_event : Event) : void {
			Tweener.addTween(btnVolume, {alpha:1, time:0.5, delay:0.5, x:btnMute.x, y:btnMute.y + btnVolume.height});
			removeChild(btnScreenPlay);
		}

		/**
		 * Mouse enters stage handler.
		 * 
		 * @param p_event
		 */
		private function stageEnterHandler(p_event : MouseEvent) : void {
			if (PlayerScreen.getInstance().stage.displayState == StageDisplayState.FULL_SCREEN) {
				if (contains(btnScreenPlay)) removeChild(btnScreenPlay);
				return;
			}
			addChild(btnScreenPlay);
		}
		
		/**
		 * Removes duplicate elements in the elementList which would otherwise falsify staticElementsWidth.
		 */
		private function removeDuplicate(array : Array) : Array {
			// array with no duplicates 
			var cleanedArray : Array = new Array();
			var elementOld : ControllerElement;
			var elementNew : ControllerElement;	
			var found : Boolean = false;
			for each(var obj : Object in array) {
				elementOld = obj.element as ControllerElement;
				found = false;
				// checks whether elementOld is allready in the cleanedArray and if not elementOld is pushed into the cleanedArray
				for each (var newObj : Object in cleanedArray) {
					elementNew = newObj.element as ControllerElement;
					if(elementOld == elementNew) {
						found = true;
						break;
					}
				}
				if (!found) cleanedArray.push(obj);
			}
			return cleanedArray;						
		}

		/**
		 * Executes the command bound to the given UI element on mouseClick.
		 */
		public function onInteraction(p_event : MouseEvent) : void {
			var cE : ControllerElement = p_event.target as ControllerElement;			
			var cmd : FWPlayerCommand = new FWPlayerCommand(cE.command);	
			PlayerLogic.getInstance().command(cmd);
		}

		/**
		 * Updates the volume.
		 */
		public function updateVolume(vol : Number) : void {
			var vCE : VolumeControllerElement = btnVolume as VolumeControllerElement; 
			vCE.volUpdate(vol);
		}

		/**
		 * Initializes the seekbutton of the seekbar.
		 */
		public function initSeek() : void {
			var sCE : SeekControllerElement = btnSeek as SeekControllerElement; 
			sCE.initSeekButton();
		}	

		/**
		 * Resets the color of the fullscreenbutton when leaving fullscreenmode.
		 */
		public function resetColor() : void {
			var cE : ControllerElement = btnFull as ControllerElement; 
			cE.changeColor(0x333333);
		}
	}
}
